//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Copyright 2012 Microsoft Corporation. All Rights Reserved
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.   
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

namespace Preflight.ServiceModel.Client.Storage
{
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Xml;
    using System.Xml.Linq;
    using System.Xml.Schema;

    public class EndpointDescriptionXmlProvider : IEndpointDescriptionRepository
    {
        public const string RootElement = "endpoints";
        private readonly string storageFilePath;
        private readonly IEndpointXmlRepository endpointXmlRepository;

        internal EndpointDescriptionXmlProvider(string storageFilePath, IEndpointXmlRepository endpointXmlRepository)
        {
            this.storageFilePath = storageFilePath;
            this.endpointXmlRepository = endpointXmlRepository;
        }

        protected string StorageFilePath
        {
            get { return this.storageFilePath; }
        }

        public IEnumerable<EndpointDescription> GetEndpoints()
        {
            var element = this.endpointXmlRepository.Read(this.storageFilePath);
            return from item in element.Elements("endpoint")
                   select EndpointDescription.CreateFromXml(item);
        }

        public void Upsert(EndpointDescription description)
        {
            var endpoints = this.GetEndpoints().ToList();
            endpoints.RemoveAll(item => this.AreDescriptionNamesEqual(item, description));
            endpoints.Add(description);
            var xml = this.SerializeToXml(endpoints);
            this.endpointXmlRepository.Save(this.storageFilePath, xml);
        }

        public void Remove(EndpointDescription description)
        {
            var endpoints = this.GetEndpoints().ToList();
            endpoints.RemoveAll(item => this.AreDescriptionNamesEqual(item, description));
            var xml = this.SerializeToXml(endpoints);
            this.endpointXmlRepository.Save(this.storageFilePath, xml);
        }

        public XElement GetFullDefinition()
        {
            return this.endpointXmlRepository.Read(this.storageFilePath);
        }

        public void SaveFullDefinition(XElement definition)
        {
            ValidateEndpointsXml(definition);
            this.endpointXmlRepository.Save(this.storageFilePath, definition);
        }

        protected bool AreDescriptionNamesEqual(EndpointDescription left, EndpointDescription right)
        {
            return string.Compare(left.Name, right.Name, true, CultureInfo.InvariantCulture) == 0;
        }

        private XElement SerializeToXml(IEnumerable<EndpointDescription> endpoints)
        {
            var root = new XElement(RootElement);
            foreach (var endpoint in endpoints)
            {
                root.Add(endpoint.ToXml());
            }

            return root;
        }

        public static IEndpointDescriptionRepository Create(string path)
        {
            return Create(path, new EndpointXmlRepository());
        }

        public static IEndpointDescriptionRepository CreateSecure(string path)
        {
            return Create(path, new SecureEndpointXmlRepository());
        }

        private static void ValidateEndpointsXml(XElement element)
        {
            var errors = new List<string>();
            var document = XDocument.Parse(element.ToString());
            document.Validate(
                GetEndpointsSchema(), 
                (sender, args) => errors.Add(args.Message));

            if (errors.Count > 0)
            {
                throw new InvalidEndpointsXml(errors);
            }
        }

        private static IEndpointDescriptionRepository Create(string path, IEndpointXmlRepository endpointXmlRepository)
        {
            if (File.Exists(path) == false)
            {
                var element = new XElement(RootElement);
                endpointXmlRepository.Save(path, element);
            }

            return new EndpointDescriptionXmlProvider(path, endpointXmlRepository);
        }

        private static XmlSchemaSet GetEndpointsSchema()
        {
            var schemas = new XmlSchemaSet();
            schemas.Add(string.Empty, XmlReader.Create(new StringReader(Resources.EndpointsSchema)));
            return schemas;
        }
    }
}
